package com.jike.jkgame.network;

import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Encryption {
	
	private static final int SEED = 31;
	private static final int DEFAULT_OFFSET = 17;
	
	private Encryption() throws IllegalAccessException {
		throw new IllegalAccessException();
	}
	
	public static byte[] getMD5Digest(InputStream is) throws IOException {
		MessageDigest md = null;
		byte[] digest = null;
		
		byte[] buffer = new byte[8192];
		int read = 0;
		try {
			md = MessageDigest.getInstance("MD5");
			while( (read = is.read(buffer)) > 0) {
				md.update(buffer, 0, read);
			}		
			digest = md.digest();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return digest;
	}

	private static byte[] getMD5Digest(byte[] msg) {
		MessageDigest md = null;
		byte[] digest = null;
		try {
			md = MessageDigest.getInstance("MD5");
			digest = md.digest(msg);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		
		return digest;
	}
	
	public static byte[] addMD5(byte[] bytes) {
		
		if(bytes == null) {
			return null;
		}
		
		byte[] digest = getMD5Digest(bytes);
		
		int len = bytes.length;
		byte[] msg = new byte[bytes.length + 16];
		System.arraycopy(bytes, 0, msg, 0, len);
		if (digest != null) {
			System.arraycopy(digest, 0, msg, len, 16);
		}
		return msg;
	}
	
	public static byte[] checkAndRemoveMD5(byte[] bytes) {
		if(bytes == null || bytes.length < 16) {
			return null;
		}
		
		int size = bytes.length - 16;
		byte[] msg = new byte[size];
		System.arraycopy(bytes, 0, msg, 0, size);

		byte[] digest = getMD5Digest(msg);
		for(int i=0; i < 16; ++i) {
			if (digest[i] != bytes[size + i]) {
				return null;
			}
		}
		
		return msg;
		
	}
	
	/**
	 * 加密过程如下：
	 *       1 计算MD5值，附加在原信息后面
	 *       2 给原信息的每个byte增加一个偏移，偏移量的计算方法：
	 *              使用信息的长度（包含MD5）域某个固定的质数求模（length ％ SEED）， 如果此模值为0，则取固定的默认值。
	 *       3 通过上面加密后，使用Base64编码
	 * @param text
	 *                     原信息
	 * @return
	 *                    加密过后的信息
	 */
	public static String encrypty(String text) {
		byte[] bytes = addMD5( text.getBytes() );
		
		int length = bytes.length;
		int offset = length % SEED;
		if (offset == 0) {
			offset = DEFAULT_OFFSET;
		}
		
		for (int i=0; i<length; ++i) {
			bytes[i] = (byte) ((bytes[i] + offset) & 0xFF);
		}
		return Base64.encodeBytes(bytes);
	}
	
	/**
	 * 解密函数，是加密的逆过程：
	 * 	      1 首先使用Base64解码
	 *        2 对加密过的byte减去一个偏移量，偏移量的计算方法同加密过程
	 *        3 检查MD5值
	 * @param code
	 *                   加密过的信息
	 * @return
	 *                   返回解码过的信息，如果MD5值不一致，则返回null
	 * @throws IOException
	 */
	public static byte[] decrypt(byte[] code) throws IOException{
		byte[] bytes = Base64.decode(code);
		int length = bytes.length;
		int offset = length % SEED;
		if (offset == 0) {
			offset = DEFAULT_OFFSET;
		}
		
		for (int i=0; i<length; ++i) {
			bytes[i] = (byte) ((256 + bytes[i] - offset) & 0xFF);
		}
		
		return checkAndRemoveMD5(bytes);
	}
	
	public static String decrypt(String code) throws IOException{
        byte[] bytes = Base64.decode(code);
        int length = bytes.length;
        int offset = length % SEED;
        if (offset == 0) {
            offset = DEFAULT_OFFSET;
        }
        
        for (int i=0; i<length; ++i) {
            bytes[i] = (byte) ((256 + bytes[i] - offset) & 0xFF);
        }
        
        byte[] result = checkAndRemoveMD5(bytes);
        
        if (result == null) {
            return null;
        } else {
            return new String(result);
        }
        
    }
	
	
	
	
}
